package cn.bigfire.wzdj.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.ZipUtil;
import cn.bigfire.wzdj.pojo.bo.WZDJHeader;
import cn.bigfire.wzdj.pojo.bo.WZDJPack;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;

@Slf4j
public class PackUtil {

    /**
     * 封包并发送
     *
     * @param channel  channel
     * @param id       id
     * @param wzdjBody wzdjBody
     */
    public static <WZDJBody> void send(Channel channel, int id, WZDJBody wzdjBody) {
        //header
        WZDJHeader wzdjHeader = WZDJHeader.ofById(id);
        //body
        WZDJPack<WZDJBody> wzdjPack = new WZDJPack<>(wzdjHeader, wzdjBody);
        log.info(wzdjPack.toString());
        channel.writeAndFlush(wzdjPack);
//        ByteBuf buffer = Unpooled.buffer();
//        byte[] bytes = wzdjPack.getBytes();
//        log.info(ByteUtil.getSize(bytes.length));
//        buffer.writeBytes(bytes);
//        BinaryWebSocketFrame binaryWebSocketFrame = new BinaryWebSocketFrame(buffer);
//        channel.writeAndFlush(binaryWebSocketFrame);
    }


    public static <WZDJBody> void send(Channel channel, WZDJBody wzdjBody) {
        //header
        String simpleName = wzdjBody.getClass().getSimpleName();
        int id = IdMaps.getIdByType(simpleName);
        WZDJHeader wzdjHeader = WZDJHeader.ofById(id);
        //body
        WZDJPack<WZDJBody> wzdjPack = new WZDJPack<>(wzdjHeader, wzdjBody);
        log.info("send:" + wzdjPack);
        channel.writeAndFlush(wzdjPack);
        ThreadUtil.sleep(100);
    }




    //解包---------------------------------------------------------------------------------------------------------------

    public static HashSet<Integer> ignoreCmd = new HashSet<>();
    static {
        /*
        [1]cmd65536039_RpcClientFightHitDmg 响应次数95415,总耗时:1158604,平均耗时:12.142786773568098
        [2]cmd65536042_RpcClientFightSkill 响应次数78790,总耗时:1000924,平均耗时:12.70369336210179
        [3]cmd65536018_RpcClientFightActorAttrs 响应次数57234,总耗时:702795,平均耗时:12.279326973477303
        [4]cmd65536023_RpcClientFightRemoveActors 响应次数36683,总耗时:456760,平均耗时:12.451544312079164  有用
        [5]cmd65536040_RpcClientFightCreateBullet 响应次数35996,总耗时:445980,平均耗时:12.389709967774197
        [6]cmd65536019_RpcClientFightSyncResource 响应次数17019,总耗时:219647,平均耗时:12.905987425818203  有用
        [7]cmd65536005_RpcClientFightFrameBegin 响应次数10999,总耗时:144990,平均耗时:13.182107464314937
        [8]cmd65536006_RpcClientFightFrameEnd 响应次数10999,总耗时:136038,平均耗时:12.3682152922993
         */
        ignoreCmd.add(65536039);//命中 一局大概9万次
        ignoreCmd.add(65536042);//技能 一局大概7万次
        ignoreCmd.add(65536018);//战机属性 一局大概5万次
//        ignoreCmd.add(65536023);//有用
        ignoreCmd.add(65536040);
//        ignoreCmd.add(65536019);//有用
        ignoreCmd.add(65536005);
        ignoreCmd.add(65536006);
    }

    public static List<WZDJPack<?>> decodeBytes(byte[] data) {
        //字节长度<8直接报错
        if (data.length < WZDJHeader.HEADER_LENGTH) {
            throw new IllegalArgumentException("数据长度不能小于8");
        }
        List<WZDJPack<?>> packList = new ArrayList<>();

        byte[] headerBytes = ByteUtil.subBefore(data, WZDJHeader.HEADER_LENGTH);
        WZDJHeader wzdjHeader = WZDJHeader.of(headerBytes);
        if (data.length < 12) { //字节数在8-12之间 没body
            packList.add(new WZDJPack(wzdjHeader, null));
            return packList;
        }
        int bodyLenStart = WZDJHeader.HEADER_LENGTH;
        if (wzdjHeader.isNeedCRC()) {
            bodyLenStart++;
        }
        int bodyLenEnd = bodyLenStart + 4;
        byte[] bodyLenBytes = ByteUtil.subBetween(data, bodyLenStart, bodyLenEnd);
        int len = cn.hutool.core.util.ByteUtil.bytesToInt(bodyLenBytes);
        if (len <= 0) {//有len但没body
            packList.add(new WZDJPack(wzdjHeader, null));
            return packList;
        }

        byte[] bodyBytes = ByteUtil.subAfter(data, bodyLenEnd);//剩余的数据
        if (wzdjHeader.getCompressType() == 1) {
            try {
//                int beforeSize = bodyBytes.length;
                bodyBytes = ZipUtil.unGzip(bodyBytes);
//                int unGzipSize = bodyBytes.length;
//                System.out.printf("beforeSize:%s,unGzipSize:%s%n", beforeSize, unGzipSize);
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(0);
            }
        }

//        System.out.println(Console.where(1) + "header:" + wzdjHeader);
        //版本0 数据解包
        if (wzdjHeader.getMagic() == 1 && wzdjHeader.getVersion() == 0) {
            if (wzdjHeader.getBatch() == 1) {//fight服务器频率高的适合会合并数据包
                List<WZDJPack<?>> wzdjPacks = decodeBatchBody(bodyBytes);
                packList.addAll(wzdjPacks);
            } else {//大厅大多都是单数据包的
                WZDJPack<?> wzdjPack = WZDJPack.build(wzdjHeader, bodyLenBytes, bodyBytes);
                packList.add(wzdjPack);
            }
        } else {//其他版本数据解包
            //如果又其他版本说明有问题.
            System.err.println(Console.where(1)+"协议可能该更新了，IdMaps.java 里更新一下。如果更新协议还不行就该重新debug协议了");
        }


        return packList;
    }

    private static List<WZDJPack<?>> decodeBatchBody(byte[] bodyBytes) {
        List<WZDJPack<?>> packList = new ArrayList<>();

        while (true) {
            List<WZDJPack<?>> wzdjPackList = decodeSubBody(bodyBytes);
            if (bodyBytes == null || wzdjPackList == null || wzdjPackList.isEmpty()) {
                break;
            }
            int subPackLen = 0;
            for (WZDJPack<?> wzdjPack : wzdjPackList) {
                if (!ignoreCmd.contains(wzdjPack.getHeader().getId())) {//忽略 战斗中一部分 耗时且没有用 的协议
                    packList.add(wzdjPack);
                }
                subPackLen += wzdjPack.getBytes().length;
            }
            bodyBytes = ByteUtil.subAfter(bodyBytes, subPackLen);
        }
        return packList;
    }

    private static  List<WZDJPack<?>> decodeSubBody(byte[] data) {
        List<WZDJPack<?>> packList = new ArrayList<>();
        try {
            if (data.length <= WZDJHeader.HEADER_LENGTH) {
//            log.error("数据不足8字节");
                return null;
            }
            byte[] headerBytes = ByteUtil.subBetween(data, 0, WZDJHeader.HEADER_LENGTH);
            WZDJHeader wzdjHeader = WZDJHeader.of(headerBytes);

            if (wzdjHeader.isHeartbeat()) {//batch中有心跳包
                log.error("batch中有心跳包" + wzdjHeader.getTime());
                return null;
            }
            int bodyLenStart = WZDJHeader.HEADER_LENGTH;
            if (wzdjHeader.getCrc() == 1) {
                bodyLenStart++;
            }
            int bodyLenEnd = bodyLenStart + 4;
            if (bodyLenEnd >= data.length) {
                String headerStr = HexUtil.encodeHexStr(headerBytes);
                String hexStr = HexUtil.encodeHexStr(data);
                System.err.printf("header后面去不到4字节的len字段, headerStr:%s,header:%s,data:%s%n", wzdjHeader.toString(), headerStr, hexStr);
                return null;
            }

//            System.out.println(Console.where(1) + "header:" + wzdjHeader);
            byte[] bodyBytes = ByteUtil.subAfter(data, bodyLenEnd);//剩余的数据
//            System.out.println(Console.where(1) +"后续数据"+ HexUtil.encodeHexStr(bodyBytes));
            if (wzdjHeader.getCompressType() == 1) {
                try {
//                int beforeSize = bodyBytes.length;
                    bodyBytes = ZipUtil.unGzip(bodyBytes);
//                int unGzipSize = bodyBytes.length;
//                System.out.printf("beforeSize:%s,unGzipSize:%s%n", beforeSize, unGzipSize);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.exit(0);
                }
            }
//            System.out.println(Console.where(1) + "header:" + wzdjHeader);
            //版本0 数据解包
            if (wzdjHeader.getMagic() == 1 && wzdjHeader.getVersion() == 0) {
                if (wzdjHeader.getBatch() == 1) {//fight服务器频率高的适合会合并数据包
                    List<WZDJPack<?>> wzdjPacks = decodeBatchBody(bodyBytes);
                    packList.addAll(wzdjPacks);
                } else {//大厅大多都是单数据包的
                    byte[] bodyLenBytes = ByteUtil.subBetween(data, bodyLenStart, bodyLenEnd);
                    int bodyLen = cn.hutool.core.util.ByteUtil.bytesToInt(bodyLenBytes);
                    if (bodyLen < 0 || bodyLen > data.length) {//有len但没body
                        System.err.println("bodyLen:" + bodyLen + "  不合法");
                        log.error("bodyLen:" + bodyLen + "  不合法");
                        return null;
                    }
                    int bodyStart = bodyLenEnd;
                    int bodyEnd = bodyStart + bodyLen;
//        byte[] bodyBytes = ByteUtil.subAfter(data, bodyLenEnd);//此处不能用 剩余的数据 因为后面可能还有其他数据包
                    byte[] subBodyBytes = ByteUtil.subBetween(data, bodyStart, bodyEnd);//只截取一部分
                    if (wzdjHeader.getCompressType() == 1 && subBodyBytes.length > 0) {
                        int beforeSize = subBodyBytes.length;
                        subBodyBytes = ZipUtil.unGzip(subBodyBytes);
                        int unGzipSize = subBodyBytes.length;
                        System.out.printf("beforeSize:%s,unGzipSize:%s%n", beforeSize, unGzipSize);
                    }
                    WZDJPack wzdjPack = WZDJPack.build(wzdjHeader, bodyLenBytes, subBodyBytes);
                    packList.add(wzdjPack);
                }
            } else {//其他版本数据解包
                //如果又其他版本说明有问题.
                System.err.println(Console.where(1)+"协议可能该更新了，IdMaps.java 里更新一下。如果更新协议还不行就该重新debug协议了");
            }

            return packList;
        } catch (Exception e) {
            log.error("decodeSubBody error:" + e);
//            e.printStackTrace();
        }
        return packList;
    }

}
